<?php
/**
 * Created by PhpStorm.
 * User: 9ey
 * Date: 2019/6/14
 * Time: 16:32
 */

namespace App\Excel;


use App\Exceptions\SysException;
use App\Facade\AppContext;
use App\Facade\System\File;
use App\Models\Log\Import;
use App\Repositories\System\FileRepository;
use App\Utils\Output;
use Box\Spout\Common\Entity\Cell;
use Box\Spout\Common\Type;
use Box\Spout\Reader\Common\Creator\ReaderFactory;
use Box\Spout\Writer\Common\Creator\WriterFactory;
use Closure;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\ValidationException;
use Throwable;
use const PATHINFO_EXTENSION;
use const PATHINFO_FILENAME;

class ImportHandler
{
    const CACHE_KEY = 'read';
    /**
     * 纯导入模式
     */
    const MODE_ADD = 1;
    /**
     * 纯修改模式
     */
    const MODE_MODIFY = 2;
    /**
     * 保存模式，可添加和修改
     */
    const MODE_SAVE = 3;
    /**
     * 类型
     * @var string
     */
    private $type = '';
    /**
     * 导入模式
     * @var int
     */
    private $mode = 1;
    /**
     * 标题模板
     * @var array
     */
    private $titleCols = [];
    /**
     * 列数量
     * @var int
     */
    private $colNumber = 0;
    /**
     * 标题总行数
     * @var int
     */
    private $titleRawCount = 1;
    /**
     * @var UploadedFile
     */
    private $file;
    /**
     * @var Closure
     */
    private $importDataFunc = false;
    /**
     * 异常信息
     * @var bool
     */
    private $errorInfo = false;
    /**
     * 数据
     * @var array
     */
    public $data = [];
    public $otherError = [];
    public $updateNumber = 0;

    static function get($type): ImportHandler
    {
        $inc = new self();
        $inc->type = $type;
        return $inc;
    }

    /**
     * @param int $colNumber
     * @return ImportHandler
     */
    public function setColNumber(int $colNumber): ImportHandler
    {
        $this->colNumber = $colNumber;
        return $this;
    }

    /**
     * 设置标题列
     * @param array $titleCols 一个三维数组
     * @return ImportHandler
     */
    public function setTitleCols(array $titleCols): ImportHandler
    {
        $this->titleCols = $titleCols;
        $this->titleRawCount = count($titleCols);
        $this->colNumber = count($titleCols[$this->titleRawCount - 1]);
        return $this;
    }

    public function check()
    {
        set_time_limit(0);
        ini_set('memory_limit', '1512M');
        ini_set('max_execution_time', 0);
        $file = request()->file('file');
        if ($file->getSize() >= 5 * 1024 * 1024) {
            $this->errorInfo = '建议文件大小在5MB内,当前文件大小:' . round($file->getSize() / 1024 / 1024, 2) . 'MB';
            return false;
        }
        $this->file = $file;
        return true;
    }

    /**
     * 导入
     * @param $call
     * @return Output
     * @throws SysException
     */
    public function import($call)
    {
        $file = $this->file;
        $reportName = $file->getClientOriginalName();
        $successCount = 0;
        $totalNumber = 0;
        $errorNumber = 0;
        $updateNumber = 0;
        $error_file_id = 0;
        $upload = new FileRepository();
        $uploadResult = $upload->file([$file], 1);

        if ($uploadResult->isSuccess()) {
            //上传文件
            $original_file_id = $uploadResult->data[0]['id'];
        } else {
            //上传失败
            throw new SysException($uploadResult->msg);
        }
        try {
            if ($this->errorInfo) {
                throw new SysException($this->errorInfo);
            }
            $reader = ReaderFactory::createFromType(Type::XLSX);
            $reader->setShouldPreserveEmptyRows(true);
            $reader->open(File::getLocalPath($original_file_id));
            $writer = WriterFactory::createFromType(Type::XLSX);
            //创建文件
            $dir = storage_path('app' . DIRECTORY_SEPARATOR . 'excel') . DIRECTORY_SEPARATOR;
            if (!file_exists($dir))
                \Illuminate\Support\Facades\File::makeDirectory($dir, 0777, true);
            $errorPath = $dir . 'import_' . $original_file_id . '_error.xlsx';
            $writer->openToFile($errorPath);//输出到文件
            $sheetIndex = 0;
            $emptyRows = [];
            $error = [];
            foreach ($reader->getSheetIterator() as $sheet) {
                if ($sheetIndex > 0) {
                    break;
                }
                $sheetIndex++;
                $emptyNumber = 0;
                $key = 0;
                foreach ($sheet->getRowIterator() as $row) {
                    $key++;
                    $item = $row->toArray();
                    if (count($item) < $this->colNumber) {
                        for ($i = count($item); $i < $this->colNumber; $i++) {
                            $item[$i] = '';
                        }
                    }
                    if ($key <= $this->titleRawCount) {
                        //写入错误日志
                        $row->setCellAtIndex(new Cell('错误原因'), $this->colNumber);
                        $writer->addRow($row);
                        #验证模板是否符合规范
                        if (count($this->titleCols)) {
                            $cols = $this->titleCols[$key - 1];
                            foreach ($cols as $i => $t) {
                                if (!isset($item[$i]) || $item[$i] != $t) {
                                    throw new SysException(__('common.tips.please_download_the_latest_template_to_import'));
                                }
                            }
                        } else {
                            if (count($item) < $this->colNumber) {
                                throw new SysException(__('common.tips.please_download_the_latest_template_to_import'));
                            }
                        }
                        continue;
                    }
                    $rowNumber = $key;
                    $totalNumber++;
                    if (!count(array_filter($item))) {
                        $error[] = ['row' => $rowNumber, 'description' => '没有数据'];
                        $emptyNumber++;
                        $emptyRows[] = $row;
                        continue;
                    }
                    foreach ($emptyRows as $emptyRow) {
                        $emptyRow->setCellAtIndex(new Cell('没有数据'), $this->colNumber);
                        $writer->addRow($emptyRow);
                    }
                    $emptyRows = [];
                    $emptyNumber = 0;
                    foreach ($item as $k => $v) {
                        $item[$k] = trim($v);
                    }
                    try {
                        #执行业务处理
                        $call($item, $rowNumber, $this);
                        $successCount++;
                    } catch (ValidationException $exception) {
                        $reason = $exception->validator->errors()->first();
                        $row->setCellAtIndex(new Cell($reason), $this->colNumber);
                        $writer->addRow($row);
                        $error[] = ['row' => $rowNumber, 'description' => $reason];
                    } catch (SysException $e) {
                        $reason = $e->getMessage();
                        $row->setCellAtIndex(new Cell($reason), $this->colNumber);
                        $writer->addRow($row);
                        $error[] = ['row' => $rowNumber, 'description' => $reason];
                    } catch (Throwable $e) {
                        $reason = $e->getMessage();
                        $row->setCellAtIndex(new Cell($reason), $this->colNumber);
                        $writer->addRow($row);
                        $error[] = ['row' => $rowNumber, 'description' => $reason];
                        Log::info($e->getMessage() . "\n" . $e->getTraceAsString());
                    }
                }
                while ($emptyNumber) {
                    array_pop($error);
                    $emptyNumber--;
                    $totalNumber--;
                }

                if ($this->importDataFunc) {
                    $func = $this->importDataFunc;
                    $func($this);
                    if (count($this->otherError)) {
                        foreach ($sheet->getRowIterator() as $rowIndex => $row) {
                            if (isset($this->otherError[$rowIndex])) {
                                $reason = $this->otherError[$rowIndex];
                                $row->setCellAtIndex(new Cell($reason), $this->colNumber);
                                $writer->addRow($row);
                                $error[] = ['row' => $rowIndex, 'description' => $reason];
                                $successCount--;
                                unset($this->otherError[$rowIndex]);
                            }
                        }
                    }
                }
                $writer->close();
                $errorNumber = count($error);
                if ($errorNumber) {
                    //有错误上传错误文件
                    $uploadResult = $upload->file([new UploadedFile($errorPath, pathinfo($reportName, PATHINFO_FILENAME) . "错误数据." . pathinfo($reportName, PATHINFO_EXTENSION))], 1);
                    if ($uploadResult->isSuccess()) {
                        //上传文件
                        $error_file_id = $uploadResult->data[0]['id'];
                    } else {
                        //上传失败
                        throw new SysException($uploadResult->msg);
                    }
                }
            }
            $logId = $this->store([
                'file_name' => $reportName,
                'total' => $totalNumber,
                'success' => $successCount,
                'error' => $errorNumber,
                'import_time' => time(),
                'original_file_id' => $original_file_id,
                'error_file_id' => $error_file_id
            ]);
            if ($this->mode == self::MODE_SAVE) {
                $msg = $this->getMsgHtml($logId, $successCount, $errorNumber, $updateNumber);
            } else {
                $msg = $this->getMsgHtml($logId, $successCount, $errorNumber);
            }
            return Output::fail($msg, -1, ['data' => array_values($this->data)]);
        } catch (Throwable $e) {
            Log::info($e->getMessage() . "\r\n" . $e->getTraceAsString());
            return Output::error($e, '');
        }
    }

    function getMsgHtml($errLogId, $validQty, $errorNumber, $updateNumber = false)
    {
        $url = '';
        if ($errLogId) {
            $url = '<a class="layui-btn layui-btn-xs layui-btn-normal"  href="' . url('archives/admin/product/import/export') . '/' . $errLogId . '">下载</a>';
        }
        if ($updateNumber === false) {
            $msg = '<p>导入成功条数：' . $validQty . '</p><p>导入错误条数：' . $errorNumber . '</p><p>错误报表链接：' . $url . '</p>';
        } else {
            $msg = '<p>成功添加条数：' . $validQty . '</p><p>成功修改条数：' . $updateNumber . '</p><p>导入错误条数：' . $errorNumber . '</p><p>错误报表链接：' . $url . '</p>';
        }
        return $msg;
    }

    private function store($data)
    {
        $data['user_id'] = AppContext::userId();
        $data['type'] = $this->type;
        $log = Import::create($data);
        return $log['id'];
    }

    public function addError($rowNumber, $reason)
    {
        $this->otherError[$rowNumber] = $reason;
    }

    /**
     * @param Closure $importDataFunc
     * @return ImportHandler
     */
    public function setImportDataFunc(Closure $importDataFunc)
    {
        $this->importDataFunc = $importDataFunc;
        return $this;
    }

    /**
     * @param int $mode
     * @return ImportHandler
     */
    public function setMode(int $mode)
    {
        $this->mode = $mode;
        return $this;
    }
}